#include "stdafx.h"
#include <fcntl.h>
#include <io.h>
#include <core/Convert.hpp>
#include <vfs/VirtualProfile.h>
#include <vfs/SlfLibraryLocation.h>
#include <vfs/DirectoryLocation.h>
#include <utils/INIReader.h>
#include <utils/ErrorMessage.h>
#include <sgp/RandomManager.h>
#include <sgp/Settings.h>
#include <sgp/InputManager.h>
#include <sgp/VideoObjectManager.h>
#include <sgp/VideoManager.h>
#include <sgp/TimerManager.h>
#include <sgp/ClockManager.h>
#include <sgp/ImageManager.h>
#include <sgp/FontManager.h>
#include <sgp/CursorManager.h>
#include <sgp/MouseRegionManager.h>
#include "Splash.h"
#include "GameManager.h"

#if 0
#	include "Stackwalker.h"
#	define JA2_ALLOC_DEBUG
#endif

#ifdef JA2_ALLOC_DEBUG
class alloc
{
public:
	~alloc()
	{
		DeInitAllocCheck();
	}
} g_alloc;
#endif

namespace ja2
{
	namespace
	{
		//! Global variables used by window procedure
		sgp::InputManager *g_pInputManager = nullptr;
		//! Is application active.
		bool g_application_active = true;
		//! Game manager instance used by window proc.
		GameManager *g_pGameManager = nullptr;
		//! Get content of file.
		std::string get_file_content(boost::filesystem::path const &Path)
		{
			std::ifstream stream(Path.string().c_str(), std::ios_base::in);
			std::string content, line;
			while(std::getline(stream, line))
				content.append(line).push_back('\n');

			return std::move(content);
		}
		// Window procedure
		LRESULT CALLBACK wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
		{
			switch(uMsg)
			{
			case WM_CREATE:
				break;
			case WM_CLOSE:
				PostQuitMessage(0);
				break;
			case WM_SETCURSOR:
				SetCursor( NULL);
				return TRUE;
			case WM_TIMER:
#ifdef LUACONSOLE
//x				PollConsole( );
#endif
				// Run the one frame
				if(g_application_active)
					g_pGameManager->run();
				break;
			default:
				return DefWindowProc(hwnd, uMsg, wParam, lParam);
			}
#if 0
			static BOOLEAN fRestore = FALSE;

			if ( Message == WM_USER )
			{
				FreeConsole();
				return 0L;
			}
			//	BOOL visible = IsWindowVisible(hWindow);

			if(gfIgnoreMessages)
				return(DefWindowProc(hWindow, Message, wParam, lParam));

			// ATE: This is for older win95 or NT 3.51 to get MOUSE_WHEEL Messages
			if ( Message == guiMouseWheelMsg )
			{
				QueueEvent(MOUSE_WHEEL, wParam, lParam);
				return( 0L );
			}

			switch(Message)
			{
			
			case WM_MOVE:
				// if( 1==iScreenMode )
				{
					GetClientRect(hWindow, &rcWindow);
					// Go ahead and clamp the client width and height
					rcWindow.right = SCREEN_WIDTH;
					rcWindow.bottom = SCREEN_HEIGHT;
					ClientToScreen(hWindow, (LPPOINT)&rcWindow);
					ClientToScreen(hWindow, (LPPOINT)&rcWindow+1);
					int xPos = (int)(short) LOWORD(lParam); 
					int yPos = (int)(short) HIWORD(lParam);
					BOOL needchange = FALSE;
					if (xPos < 0)
					{
						xPos = 0;
						needchange = TRUE;
					}
					if (yPos < 0)
					{
						yPos = 0;
						needchange = TRUE;
					}
					if (needchange)
					{
						SetWindowPos( hWindow, NULL, xPos, yPos, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
					}

				}
				break;
			case WM_GETMINMAXINFO:
				{
					MINMAXINFO *mmi = (MINMAXINFO*)lParam;

					mmi->ptMaxSize = ptWindowSize;
					mmi->ptMaxTrackSize = mmi->ptMaxSize;
					mmi->ptMinTrackSize = mmi->ptMaxSize;
					break;
				}


			

			case WM_ACTIVATEAPP: 
				switch(wParam)
				{
				case TRUE: // We are restarting DirectDraw
					if (fRestore == TRUE)
					{
						RestoreVideoManager();
						RestoreVideoSurfaces();	// Restore any video surfaces

						// unpause the JA2 Global clock
						if ( !gfPauseDueToPlayerGamePause )
						{
							PauseTime( FALSE );
						}
						gfApplicationActive = TRUE;
					}
					break;
				case FALSE: // We are suspending direct draw
					if (iScreenMode == 0)
					{
						// pause the JA2 Global clock
						//PauseTime( TRUE );
						SuspendVideoManager();

						// suspend movement timer, to prevent timer crash if delay becomes long
						// * it doesn't matter whether the 3-D engine is actually running or not, or if it's even been initialized
						// * restore is automatic, no need to do anything on reactivation
						// gfApplicationActive = FALSE;
						fRestore = TRUE;
					}
					break;
				}
				break;

			case WM_DESTROY: 
				ShutdownStandardGamingPlatform();
				//		ShowCursor(TRUE);
				PostQuitMessage(0);
				break;

			case WM_SETFOCUS:
				//if (iScreenMode == 0)
				{
					RestoreCursorClipRect( );
				}
				break;

			case WM_KILLFOCUS:
				if (iScreenMode == 0)
				{
					// Set a flag to restore surfaces once a WM_ACTIVEATEAPP is received
					fRestore = TRUE;
				}
				break;

			case	WM_DEVICECHANGE:
				{
					//DEV_BROADCAST_HDR	*pHeader = (DEV_BROADCAST_HDR	*)lParam;

					////if a device has been removed
					//if( wParam == DBT_DEVICEREMOVECOMPLETE )
					//{
					//	//if its	a disk
					//	if( pHeader->dbch_devicetype == DBT_DEVTYP_VOLUME )
					//	{
					//		//check to see if the play cd is still in the cdrom
					//		if( !CheckIfGameCdromIsInCDromDrive() )
					//		{
					//		}
					//	}
					//}
				}
				break;

			case WM_SYSKEYUP:
			case WM_KEYUP:
				KeyUp(wParam, lParam);
				break;

			case WM_SYSKEYDOWN:
			case WM_KEYDOWN:
#ifdef USE_CODE_PAGE
				if(s_DebugKeyboardInput)
				{
					static Log& debugKeys = *Log::Create(L"DebugKeys.txt");
					static int input_counter = 1;
					debugKeys << (input_counter++) << " : " << (int)wParam << Log::endl;
				}
#endif // USE_CODE_PAGE
				KeyDown(wParam, lParam);
				gfSGPInputReceived =	TRUE;
				break;

			case WM_CHAR:
				{
					// WANNE: We disable this for now in multiplayer, because user could enter "\" for the file transfer path
					if (!is_networked)
					{
						if (wParam == '\\' && lParam && KF_ALTDOWN)
						{
#if USE_CONSOLE
							g_Console.Create(ghWindow);
							cout << "LUA console ready" << endl;
							cout << "> ";

							// Reset the pressed keys
							gfKeyState[ ALT ] = FALSE;						
							gfKeyState[ 219 ] = FALSE;	// "\"
#endif
						}
					}				
				}
				break;

			case WM_INPUTREADY:
				{
					wstring *tstr = (wstring*) lParam;
					if (EvalLua( tstr->c_str()))
					{
						tstr->erase();
					}
					else
					{
						cout << ">";
					}

					cout << "> ";
				}
				break;
			}
#endif
			return 0;
		}
	}

	static void init_write_profile(vfs::VirtualProfile &Prof, boost::filesystem::path const& ProfileRoot)
	{
		// Create new directory
//x		std::unique_ptr<vfs::DirectoryTreeWrite> p_dir(new vfs::DirectoryTreeWrite(boost::filesystem::path(), ProfileRoot));
		// Add it to VFS
//x		vfs::VirtualFileSystem::instance()->add_location(std::move(p_dir), Prof.get_name(), true);
	}

	//! Initialize VFS.
	static void init_vfs(boost::filesystem::path const &WrkPath, boost::filesystem::path const &Path)
	{
		boost::filesystem::path const ini_path(WrkPath/Path);
		// Read vfs INI file
		utils::PropertyContainer const properties = utils::parse_ini_file(get_file_content(ini_path));
		// Create vfs log
		utils::Log log("vfs_init.log", true);
		log << "Initializing Virtual File System";
		log.endl().endl() << "reading profiles .. ";
		// Get all profiles
		auto const profiles = properties.get_section("vfs_config").get_string_vector("PROFILES");
		// No profiles found
		if(profiles.empty())
		{
			log << "Error";
			throw RuntimeException((boost::format(resources::NO_PROFILES_FOUND) % ini_path.string()).str());
		}
		else
			log << " OK";
		// Log profiles to read
		log.endl() << "  profiles to read : ";
		std::for_each(profiles.begin(), profiles.end(), [&log](std::string const &Profile){log << Profile << ", ";});
		log.endl();
		// Read all profiles
		for(auto it = profiles.begin(), end = profiles.end(); it != end; ++it)
		{
			log.endl() << "  reading profile [";
			// Build profile section
			utils::Section const &profile_section = properties.get_section("PROFILE_" + *it);
			// Get profile name
			std::string const profile_name = profile_section.get_string_value("NAME");
			log << profile_name << "] .. ";
			// Get profile root
			boost::filesystem::path const profile_root = profile_section.get_string_value("PROFILE_ROOT");
			// Get profile locations
			auto const location_section_vector = profile_section.get_string_vector("LOCATIONS");
			log << "OK";
			// Log locations to read
			log.endl() << "  locations to read : ";
			std::for_each(location_section_vector.begin(), location_section_vector.end(), [&log](std::string const &Section){log << Section << ", ";});
			log.endl().endl();
			log.flush();
			// Read all location sections
			bool const is_location_writable = profile_section.get_bool_value_default("WRITE", false);
			for(auto it_loc = location_section_vector.begin(), end_loc = location_section_vector.end(); it_loc != end_loc; ++it_loc)
			{
				// Get location section
				std::string const location_section_string("LOC_" + *it_loc);
				utils::Section const &location_section = properties.get_section(location_section_string);
				// Get location path
				boost::filesystem::path location_path = location_section.get_string_value("PATH");
				// Get mount point
				boost::filesystem::path const location_mount_point = location_section.get_string_value("MOUNT_POINT");
				// Get location type
				std::string const location_type = location_section.get_string_value("TYPE", "NOT_FOUND");
				// Is location optional
				bool const is_optional = location_section.get_bool_value_default("OPTIONAL", false);
				log << "    reading location [ ";
				vfs::ILocationPtr_t p_location;
				// Library location
				if(location_type == "LIBRARY")
				{
					log << location_type << " | " << *it_loc << " ] .. ";
					// Location not found, try VFS path
					if(location_path.empty())
					{
						location_path = location_section.get_string_value("VFS_PATH");
						// Path invalid
						if(location_path.empty() && !is_optional)
						{
							log << resources::ERROR_MSG;
							log.endl();

							throw RuntimeException((boost::format(resources::PATH_NOT_FOUND_LOCATION) % location_section_string).str());
						}
					}
					// Make path to library
					boost::filesystem::path const library_file_path = WrkPath/profile_root/location_path;
					// File doesn't exist
					if(!boost::filesystem::exists(library_file_path))
					{
						std::string const error = (boost::format(resources::LIBRARY_NOT_FOUND) % library_file_path.string()).str();
						log << error;
						log.endl();
						// Throw if not optional
						if(!is_optional)
							throw FileNotFoundException(error, library_file_path.string());
						continue;
					}
					//  Get extension
					std::string const ext = library_file_path.extension().string();
					assert(!p_location);
					try
					{
						// SLF library
						if(ext == ".slf")
							p_location.reset(new vfs::SlfLibraryLocation(library_file_path, location_mount_point));
						// 7Zip library
						else if(ext == ".7z")
							JA2_ASSERT_MESAGE(false, "Not implemented.");
						//x								p_location.reset(new vfs::CUncompressed7zLibrary(library_file_path, location_mount_point ));
					}
					catch(BasicException const &e)
					{
						log << resources::ERROR_MSG;
						log.endl();
						log << (boost::format(resources::COULD_NOT_INITIALIZE_LIBRARY) % location_path.string() % profile_name % *it_loc % library_file_path.string() % e.what()).str();
						log.endl();
						// Re-throw if not optional
						if(!is_optional)
							throw;
					}
					// Not a library file
					if(!p_location)
					{
						log << resources::ERROR_MSG;
						log.endl();
						log << (boost::format(resources::FILE_NOT_A_LIBRARY) % library_file_path.string()).str();
						log.endl();

						throw RuntimeException("Not a library.");
					}
				}
				// Directory
				else if(location_type == "DIRECTORY")
				{
					log << location_type << " | " << *it_loc << " ] .. ";
					// Compose real path
					boost::filesystem::path const real_dir_path = profile_root/location_path;
					JA2_ASSERT(!p_location);
					// Create the location
					try
					{
//x						if(is_location_writable)
//x							p_location.reset(new vfs::DirectoryTreeWrite(location_mount_point, real_dir_path));
//x						else
							p_location.reset(new vfs::DirectoryLocation(location_mount_point, real_dir_path, WrkPath));
					}
					catch(BasicException const &e)
					{
						log << resources::ERROR_MSG;
						log.endl();
						log << (boost::format(resources::COULD_NOT_INITIALIZE_DIR) % location_path % profile_name % *it_loc % real_dir_path % e.what()).str();

						throw;
					}
				}
				// Unknown location
				else if( location_type =="NOT_FOUND")
				{
					log << (boost::format(resources::NO_TYPE_SPECIFIED_FOR_LOCATION) % location_section_string).str();
					log.endl();

					throw RuntimeException("Unknown location type.");
				}
				JA2_ASSERT(p_location);
				// Add directory location
				vfs::VirtualFileSystem::instance()->add_location(std::move(p_location), profile_name, is_location_writable);
				log << resources::OK;
				log.endl();
			}
			if(is_location_writable)
			{
				vfs::VirtualProfile *p_profile = nullptr;
				// Virtual profile doesn't exist
				if(!vfs::VirtualFileSystem::instance()->profile_exists(profile_name))
					p_profile = vfs::VirtualFileSystem::instance()->create_profile(profile_name, is_location_writable);
				else
					p_profile = vfs::VirtualFileSystem::instance()->get_profile(profile_name);
				// Profile should be writable
				if(!p_profile->is_writeable())
				{
					log << (boost::format(resources::PROFILE_NOT_WRITEABLE) % profile_name).str();
					log.endl();

					throw RuntimeException("Profile not writable.");
				}
				init_write_profile(*p_profile, profile_root);
			}
		}
		log.endl() << "VFS successfully initialized";
		log.endl();
	}

	// Get runtime settings from INI file.
	static void get_runtime_settings(boost::filesystem::path const &WrkDir, ja2::sgp::Settings &Settings_)
	{
		// Read the main ini
		ja2::utils::PropertyContainer const properties = utils::parse_ini_file(get_file_content(WrkDir/"ja2.ini"));
		// Get section
		auto const &ja2_settings_section = properties.get_section("Ja2 Settings");
		// Get locale
		Settings_.m_Locale = ja2_settings_section.get_string_value("LOCALE");
//x		if(!loc.empty())
//x			THROWIFFALSE( setlocale(LC_ALL, loc.utf8().c_str()), BuildString().add(L"invalid locale : ").add(loc).get());
		// Get resolution
		Settings_.set_resolution(static_cast<std::uint_least8_t>(ja2_settings_section.get_long_value_default("SCREEN_RESOLUTION", 0)));
		std::vector<std::string> vfs_configs;
		ja2::utils::Section::StringVector_t ini_list;
		// Have vfs config
		if(ja2_settings_section.get_string_vector("VFS_CONFIG_INI", ini_list))
			std::copy(ini_list.begin(), ini_list.end(), std::back_inserter(vfs_configs));
		// Add default
		else
			vfs_configs.push_back("vfs_config.ini");
		// Set default pixel depth
		Settings_.m_PixelDepth = 32;
		// Get sgp section
		if(properties.section_exists("SGP"))
		{
			auto sgp_section = properties.get_section("SGP");
			// Get pixel depth
			if(sgp_section.exist("PIXEL_DEPTH"))
				Settings_.m_PixelDepth = static_cast<std::uint_least8_t>(sgp_section.get_long_value("PIXEL_DEPTH"));
			// Get resolution
			if(sgp_section.exist("WIDTH") && sgp_section.exist("HEIGHT"))
				Settings_.set_resolution(
					static_cast<std::uint_least16_t>(sgp_section.get_long_value("WIDTH")), static_cast<std::uint_least16_t>(sgp_section.get_long_value("HEIGHT")));
		}
		// Set screen offset
		Settings_.set_offset((Settings_.res_width() - 640) / 2, (Settings_.res_height() - 480) / 2);
		//! Should we read option
		if(!std::get<1>(Settings_.m_FullScreen) && ja2_settings_section.exist("SCREEN_MODE_WINDOWED"))
			std::get<0>(Settings_.m_FullScreen) = ja2_settings_section.get_bool_value("SCREEN_MODE_WINDOWED");
		// WANNE: Should we play the intro?
		Settings_.m_PlayIntro = ja2_settings_section.get_bool_value_default("PLAY_INTRO", true);
		// Initialize vfs
		for(auto it = vfs_configs.begin(), end = vfs_configs.end(); it != end; ++it)
			init_vfs(WrkDir, boost::filesystem::path(*it));
	}

	static void process_cmd_line(std::string const &CommandLine, ja2::sgp::Settings &Settings_)
	{
		boost::tokenizer<boost::char_separator<char>> tokenizer(CommandLine, boost::char_separator<char>("\t ="));
		for(auto it = tokenizer.begin(), end = tokenizer.end(); it != end; ++it)
		{
			//disable the sound
			if(*it == "/NOSOUND")
				std::get<0>(Settings_.m_Sound) = false;
			else if(*it == "/FULLSCREEN")
			{
				//overwrite Graphic setting from JA2_settings.ini
				std::get<0>(Settings_.m_FullScreen) = true;
				// if set TRUE, INI is no longer evaluated
				std::get<1>(Settings_.m_FullScreen) = true;
				// no resolution read from Args. Still from INI, but could be added here, too
			}
			else if(*it == "/WINDOW")
			{
				//overwrite Graphic setting from JA2_settings.ini
				std::get<0>(Settings_.m_FullScreen) = false;
				// if set TRUE, INI is no longer evaluated
				std::get<1>(Settings_.m_FullScreen) = true;
				// no resolution read from Args. Still from INI, but could be added here, too
			}
		}
	}

	static int HandledWinMain(HINSTANCE hInstance,	HINSTANCE hPrevInstance, LPSTR CommandLine, int CommandShow)
	{
		int result = 0;
#ifdef _DEBUG
		_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#	ifdef JA2_ALLOC_DEBUG
		InitAllocCheck(ACOutput_Advanced);
#	endif
#endif
		try
		{
			// Allocate a console for this app
			AllocConsole();
			// Set the screen buffer to be big enough to let us scroll text
			CONSOLE_SCREEN_BUFFER_INFO coninfo;
			GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),	&coninfo);
			coninfo.dwSize.Y = 500;
			SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
			// redirect unbuffered STDOUT to the console
			HANDLE lStdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
			int hConHandle = _open_osfhandle(reinterpret_cast<intptr_t>(lStdHandle), _O_TEXT);
			FILE *fp = _fdopen( hConHandle, "w" );
			*stdout = *fp;
			setvbuf( stdout, NULL, _IONBF, 0 );
			// redirect unbuffered STDERR to the console
			lStdHandle = GetStdHandle(STD_ERROR_HANDLE);
			hConHandle = _open_osfhandle(reinterpret_cast<intptr_t>(lStdHandle), _O_TEXT);
			fp = _fdopen( hConHandle, "w" );
			*stderr = *fp;
			setvbuf( stderr, NULL, _IONBF, 0 );
			// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
			// point to console as well
			std::ios::sync_with_stdio();
			
			// Get working path
			boost::filesystem::path const wrk_path = boost::filesystem::initial_path();
			// Make sure that only one instance of this application is running at once
			HWND hPrevInstanceWindow = FindWindowExW( NULL, NULL, Convert::to_utf16(sgp::Settings::APPLICATION_NAME).c_str(), Convert::to_utf16(sgp::Settings::APPLICATION_NAME).c_str());
			// One is found, bring it up!
			if ( hPrevInstanceWindow != NULL )
			{
				SetForegroundWindow( hPrevInstanceWindow );
				ShowWindow( hPrevInstanceWindow, SW_RESTORE );
				return 0;
			}
#ifdef _DEBUG
			// Initialize debug manager
			new ja2::Singleton<ja2::DebugManager>(new ja2::DebugManager(wrk_path));
#endif
			// Register main topic
			JA2_REGISTER_DEBUG_TOPIC(TOPIC_JA2, "Starting JA 2");
			// Auto cleanup
			std::unique_ptr<ja2::Singleton<ja2::DebugManager>> p_debug_manager_clean_up(ja2::Singleton<ja2::DebugManager>::instance_ptr());
			std::unique_ptr<vfs::VirtualFileSystem> p_vfs_clean_up(vfs::VirtualFileSystem::instance());
			// Create default settings
			ja2::sgp::Settings settings;
//x			Log::setSharedString( getGameID() );
			//Process the command line BEFORE initialization
			process_cmd_line(CommandLine, settings);
			// Mem Usage
//x			giStartMem = MemGetFree(	) / 1024;
			// Set application name
			std::wstring class_name = Convert::to_utf16(settings.APPLICATION_NAME);
			// Set window name
			char const *window_name = "Jagged Alliance 2";
			// Read in settings
			get_runtime_settings(wrk_path, settings);
			// Register and Realize our display window. The DirectX surface will eventually overlay on top of this surface.
			WNDCLASS window_class;
			window_class.style = CS_HREDRAW | CS_VREDRAW;
			window_class.lpfnWndProc = wnd_proc;
			window_class.cbClsExtra = 0;
			window_class.cbWndExtra = 0;
			window_class.hInstance = hInstance;
//x			window_class.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
			window_class.hIcon = NULL;
			window_class.hCursor = NULL;
			window_class.hbrBackground = NULL;
			window_class.lpszMenuName = NULL;
			window_class.lpszClassName = class_name.c_str();
			if(RegisterClass(&window_class) == 0)
			{
				std::string const message = (boost::format(resources::FAILED_TO_REGISTER_WND_CLASS) % utils::format_window_get_last_error("CreateWindow")).str();
				JA2_DEBUG_MESSAGE(TOPIC_JA2, DebugManager::DL_0, message.c_str());
				throw RuntimeException(message);
			}
			// Get HWND
			HWND hWnd = NULL;
			// Get a window handle for our application (gotta have on of those) Don't change this
			if(!settings.is_full_screen())
			{
				// Set window rect
				RECT window;
				window.top = 0;
				window.left = 0;
				window.right = settings.res_width();
				window.bottom = settings.res_height();
				// Set windows attributes
				DWORD style = 0, ex_style = 0;
				AdjustWindowRectEx( &window, style, FALSE, ex_style);
				OffsetRect( &window, -window.left, -window.top);
				hWnd = CreateWindowExW(WS_EX_APPWINDOW, class_name.c_str(), Convert::to_utf16(window_name).c_str(), WS_OVERLAPPEDWINDOW & (~(WS_MAXIMIZEBOX | WS_SYSMENU)), window.left, window.top, window.right, window.bottom, NULL, NULL, hInstance, NULL);
				GetClientRect(hWnd, &window);
			}
			else
				hWnd = CreateWindowExW(WS_EX_TOPMOST, class_name.c_str(), Convert::to_utf16(window_name).c_str(), WS_POPUP | WS_VISIBLE, 0, 0, settings.res_width(), settings.res_height(), NULL, NULL, hInstance, NULL);
			// Window not created
			if (hWnd == NULL)
			{
				std::string const message = (boost::format(resources::FAILED_TO_CREATE_WINDOW) % utils::format_window_get_last_error("CreateWindow")).str();
				JA2_DEBUG_MESSAGE(TOPIC_JA2, DebugManager::DL_0, message.c_str());
				throw RuntimeException(message);
			}
			// Hide the cursor for the window.
			SetCursor(NULL);
			// Display our full screen window
			ShowWindow(hWnd, CommandShow);
			UpdateWindow(hWnd);
			SetFocus(hWnd);
			// Initialize the Input Manager
			JA2_FAST_DEBUG_MESSAGE(resources::INIT_INPUT_MANAGER);
			ja2::sgp::InputManager input_manager(hWnd);
			// Set global
			g_pInputManager = &input_manager;
			// Initialize video manager
			JA2_FAST_DEBUG_MESSAGE(resources::INIT_VIDEO_MANAGER);
			ja2::sgp::VideoManager video_manager(hWnd, settings);
			// Initialize Video Object Manager
//			JA2_FAST_DEBUG_MESSAGE(resources::INIT_VIDEO_OBJECT_MANAGER);
//			ja2::sgp::VideoObjectManager video_object_manager;
			// Initialize timer manager
			ja2::sgp::TimerManager timer_manager;
			// Image manager
			sgp::ImageManager image_manager;


			ja2::vfs::VirtualFileSystem::instance()->create_virtual_location(boost::filesystem::path("Temp"), true);
			ja2::vfs::VirtualFileSystem::instance()->create_virtual_location(boost::filesystem::path("ShadeTables"), true);
			ja2::vfs::VirtualFileSystem::instance()->create_virtual_location(boost::filesystem::path("SavedGames"),true);
			//x		getVFS()->getVirtualLocation(vfs::Path(pMessageStrings[MSG_MPSAVEDIRECTORY]+3),true)->setIsExclusive(true);


#if 0
			// Snap: moved the following from InitJA2SplashScreen for clarity
			STRING512			CurrentDir;
			STRING512			DataDir;

			// Get Executable Directory
			GetExecutableDirectory( CurrentDir );

			// Adjust Current Dir
			sprintf( DataDir, "%s\\Data", CurrentDir );
			if ( !SetFileManCurrentDirectory( DataDir ) )
			{
				DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Could not find data directory, shutting down");
				return FALSE;
			}

			//Initialize the file database
			//InitializeFileDatabase( gGameLibaries, NUMBER_OF_LIBRARIES ); // ?!?! doesn't take parameters (jonathanl)
			InitializeFileDatabase();

			// Snap: Initialize the Data directory catalogue
			gDefaultDataCat.NewCat(DataDir);

			// Snap: Get the custom Data directory location from ja2.ini (if any)
			// and initialize the custom catalogue
			char customDataPath[MAX_PATH];
			if ( GetPrivateProfileString( "Ja2 Settings","CUSTOM_DATA_LOCATION", "", customDataPath, MAX_PATH, "..\\Ja2.ini" ) )
			{
				gCustomDataCat.NewCat(std::string(CurrentDir) + '\\' + customDataPath);
			}
#endif
			// Show the splash
			init_splash_screen(video_manager, image_manager, timer_manager, settings);
			// Make sure we start up our local clock (in milliseconds)
			// We don't need to check for a return value here since so far its always TRUE
			// must initialize after VideoManager, 'cause it uses ghWindow
			sgp::ClockManager clock_manager(hWnd);
			// Initialize Font Manager
			JA2_FAST_DEBUG_MESSAGE("Initializing the Font Manager");
			sgp::FontManager font_manager(image_manager, settings.m_PixelDepth, settings, sgp::FontTranslationTable::create_english());
			// Create cursor manager
			sgp::CursorManager cusrsor_manager;

//x			FastDebugMsg("Initializing Sound Manager");
//x			// Initialize the Sound Manager (DirectSound)
//x			if (InitializeSoundManager() == FALSE)
//x			{
//x				// We were unable to initialize the sound manager
//x				FastDebugMsg("FAILED : Initializing Sound Manager");
//x				return FALSE;
//x			}
			// Create mouse region manager
			sgp::MouseRegionManager mouse_region_manager;
			JA2_FAST_DEBUG_MESSAGE("Initializing Game Manager");
			GameManager game_manager(video_manager, cusrsor_manager, input_manager, mouse_region_manager);
			// Set global variable
			g_pGameManager = &game_manager;
//x			SetIntroType( INTRO_SPLASH );
			// Register mouse wheel message
//x			guiMouseWheelMsg = RegisterWindowMessage( MSH_MOUSEWHEEL );
			// Create random number manager.
//x			JA2_FAST_DEBUG_MESSAGE("Initializing Random");
//x			ja2::sgp::RandomManager random_number_man;

			bool program_running = true;
			JA2_FAST_DEBUG_MESSAGE("Running Game");
			// 0verhaul:	Use the smallest available timer to make sure all animation updates happen at the speed they're supposed to
			std::uint_least32_t const timer = 1;
			SetTimer(hWnd, timer, 1, NULL);
			// At this point the SGP is set up, which means all I/O, Memory, tools, etc... are available. All we need to do is attend to the gaming mechanics themselves
			MSG	Message;
			Message.wParam = 0;
			while (program_running)
			{
				// It's quitting time
				if (!GetMessage(&Message, NULL, 0, 0))
				{
					result = Message.wParam;
					break;
				}
				// Ok, now that we have the message, let's handle it
				TranslateMessage(&Message);
				DispatchMessage(&Message);
			}
//x				// Windows hasn't processed any messages, therefore we handle the rest
//x#	ifdef LUACONSOLE
//x				PollConsole( );
//x#	endif
//x
//x			if (gfApplicationActive == FALSE)
//x			{
//x				// Well we got nothing to do but to wait for a message to activate
//x				WaitMessage();
//x			}
//x			else
//x			{
//x				// Well, the game is active, so we handle the game stuff
//x				GameLoop();
//x				// After this frame, reset input given flag
//x				gfSGPInputReceived	=	FALSE;
//x			}
			KillTimer(hWnd, timer);
			// Make sure we unregister the last remaining debug topic before shutting down the debugging layer
			JA2_UNREGISTER_DEBUG_TOPIC(TOPIC_JA2, "Closing JA2");
			// This is the normal exit point
			JA2_FAST_DEBUG_MESSAGE("Exiting Game");
		}
		catch(std::exception const &e)
		{
			MessageBoxW(NULL, Convert::to_utf16(e.what()).c_str(), L"Unhandled exception", MB_OK | MB_ICONWARNING);
			result = -1;
		}
		catch(...)
		{
			MessageBoxW(NULL, L"Unknown exception caught", L"Unhandled exception", MB_OK | MB_ICONWARNING);
			result = -2;
		}


		return result;
	}
} /*ja2*/

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCommandLine, int sCommandShow)
{
	return ja2::HandledWinMain(hInstance, hPrevInstance, pCommandLine, sCommandShow);
}
